#!/usr/bin/python
# -*- coding: utf-8 -*-

import wx
import wx.grid as gridlib
import logging, conf

useLocalData = False
data = []
grade = []

class MyDataTable(gridlib.PyGridTableBase):
    def __init__(self, log):
        gridlib.PyGridTableBase.__init__(self)

        initGrade()
        initTags()

        self.colLabels = ['序号', '学期', '选课号', '课程代码', '课程名称',
                          '学分', '成绩', '专业课']
        
        self.dataTypes = [gridlib.GRID_VALUE_STRING] * 8
        
        self.data = []
        for line in data[1:]:
            dline = line[:]
            while len(self.data) > 1 and len(dline) < len(self.data[0]) - 1:
                dline.append('')
            dline.append('')
            self.data.append(dline)

    def GetNumberRows(self):
        return len(self.data) + 1
    
    def GetNumberCols(self):
        return len(self.data[0])
    
    def IsEmptyCell(self, row, col):
        try:
            return not self.data[row][col]
        except IndexError:
            return True
        
    def GetValue(self, row, col):
        try:
            return self.data[row][col].decode('utf-8')
        except UnicodeEncodeError:
            if not self.data[row][col]:
                return ''
            return self.data[row][col]
        except AttributeError:
            return ''
        except IndexError:
            return ''
        
    def SetValue(self, row, col, value):
        def innerSetValue(row, col, value):
            try:
                self.data[row][col] = value
            except IndexError:
                # add a new row
                self.data.append([''] * self.GetNumberCols())
                innerSetValue(row, col, value)

                # tell the grid we've added a row
                msg = gridlib.GridTableMessage(self,            # The table
                        gridlib.GRIDTABLE_NOTIFY_ROWS_APPENDED, # what we did to it
                        1                                       # how many
                        )

                self.GetView().ProcessTableMessage(msg)
        innerSetValue(row, col, value) 

    def GetColLabelValue(self, col):
        try:
            return self.colLabels[col].decode('utf-8')
        except:
            return self.colLabels[col]

    def GetTypeName(self, row, col):
        return self.dataTypes[col]

    def CanGetValueAs(self, row, col, typeName):
        colType = self.dataTypes[col].split(':')[0]
        if typeName == colType:
            return True
        else:
            return False

    def CanSetValueAs(self, row, col, typeName):
        return self.CanGetValueAs(row, col, typeName)

class MyTableGrid(gridlib.Grid):
    def __init__(self, parent, log, frame):
        gridlib.Grid.__init__(self, parent, -1)

        self.sortBy = 0
        self.frame = frame
        table = self.table = MyDataTable(log)
        
        self.SetTable(table, True)
        self.SetRowLabelSize(0)
        self.SetMargins(0, 0)
        self.AutoSizeColumns(False)

        width = (50, 170, 120, 120, 150, 50, 50, 50)
        for i, w in enumerate(width):
            self.SetColSize(i, w)

        self.colors = [(wx.WHITE, wx.GREEN), (wx.BLACK, wx.WHITE), (wx.CYAN, wx.WHITE),
                  (wx.BLUE, wx.WHITE), (wx.RED, wx.WHITE), (wx.BLACK, wx.WHITE)]
        
        self.resetColor()

        gridlib.EVT_GRID_CELL_LEFT_CLICK(self, self.OnLeftClick)
        gridlib.EVT_GRID_LABEL_LEFT_CLICK(self, self.OnLabelClick)

    def OnLabelClick(self, evt):
        col = evt.GetCol()
        if self.sortBy % 100 == col:
            self.sortBy = (self.sortBy + 100) % 200
        else:
            self.sortBy = col
        logging.debug('Label %d clicked, sortBy = %d' % (evt.GetCol(), self.sortBy))
        reverse = 1 if self.sortBy >= 100 else -1
        self.table.data.sort(lambda x, y: reverse * cmp(x[col], y[col]))
        self.resetColor()
        
    def OnLeftClick(self, evt):
        row = evt.GetRow()
        col = evt.GetCol()
        col = self.table.GetNumberCols() - 1
        if col == self.table.GetNumberCols() - 1:
            if self.GetCellValue(row, col) == '':
                self.SetCellValue(row, col, u'是')
            else: 
                self.SetCellValue(row, col, '')
            self.frame.refreshDisplay()
            
    def resetColor(self, evt = None):
        ''' Reset row colors. attrlist is rebuilt at each call, since if we remove
        the rebuilding routines there will be exceptions 
        '''
        try:
            grade = map(lambda x: conf.letterToPoint(x[6]), self.table.data)
        except Exception, e:
            logging.error(e)
            
        attrlist = []
        
        for i, (fg, bg) in enumerate(self.colors):
            attr = gridlib.GridCellAttr()
            attr.SetBackgroundColour(bg)
            attr.SetTextColour(fg)
            attrlist.append(attr)

        if self.frame.cbxColor.IsChecked():
            for i, y in enumerate(grade):
                if y < 0:
                    continue
                else:
                    self.SetRowAttr(i, attrlist[int(y + 0.5)])
        else:
            for i, y in enumerate(grade):
                if y < 0:
                    continue
                else:
                    self.SetRowAttr(i, attrlist[-1])
        self.Refresh()
        

                
class MyFrame(wx.Frame):
    def __init__(self, parent, log):
        wx.Frame.__init__(self, parent, -1, u'绩点查询工具 %s' % conf.version, size=(900, 640))

        p = wx.Panel(self, -1, style = 0)

        self.cbxColor = wx.CheckBox(p, -1, u'颜色区别')
        self.cbxColor.SetValue(False)

        grid = self.grid = MyTableGrid(p, log, self)
        
        bsLeft = wx.BoxSizer(wx.VERTICAL)
        bsRight = wx.BoxSizer(wx.VERTICAL)
        bs = wx.BoxSizer(wx.HORIZONTAL)
        
        fontLabel = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.BOLD, faceName=u'楷体_GB2312')
        fontScore = wx.Font(14, wx.DECORATIVE, wx.NORMAL, wx.NORMAL)
        fontAuthor = wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
        
        lblGPA = wx.StaticText(p, -1, u'总绩点')
        lblGPA.SetFont(fontLabel)
        lblGPAS = wx.StaticText(p, -1, u'专业课绩点')
        lblGPAS.SetFont(fontLabel)
        lblCredit = wx.StaticText(p, -1, u'总学分')
        lblCredit.SetFont(fontLabel)
        lblCreditS = wx.StaticText(p, -1, u'专业课学分')
        lblCreditS.SetFont(fontLabel)

        txtPointAll = self.txtPointAll = wx.StaticText(p, -1, '')
        txtPointAll.SetFont(fontScore)
        txtPointAll.SetForegroundColour('red')
        txtPointSpec = self.txtPointSpec = wx.StaticText(p, -1, '')
        txtPointSpec.SetFont(fontScore)
        txtPointSpec.SetForegroundColour('red')
        txtCredit = self.txtCredit = wx.StaticText(p, -1, '')
        txtCredit.SetFont(fontScore)
        txtCredit.SetForegroundColour('red')
        txtCreditS = self.txtCreditS = wx.StaticText(p, -1, '')
        txtCreditS.SetFont(fontScore)
        txtCreditS.SetForegroundColour('red')
        
        txtLabel3 = wx.StaticText(p, -1, u'批量选择')
        btnSelectAll = wx.Button(p, -1, u'全选')
        btnDeselectAll = wx.Button(p, -1, u'取消选择')
       
        txtLabel4 = wx.StaticText(p, -1, 'by ZelluX')
        txtLabel4.SetFont(fontAuthor)
        
        bsLeft.Add(grid, 1, wx.EXPAND, 5)
        bsRight.Add(lblGPA)
        bsRight.Add(txtPointAll)
        bsRight.Add(lblGPAS)
        bsRight.Add(txtPointSpec)
        bsRight.Add(lblCredit)
        bsRight.Add(txtCredit)
        bsRight.Add(lblCreditS)
        bsRight.Add(txtCreditS)
        bsRight.Add(wx.StaticLine(p), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
        bsRight.Add(self.cbxColor)
        bsRight.Add(wx.StaticLine(p), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
        bsRight.Add(txtLabel3)
        bsRight.Add(btnSelectAll)
        bsRight.Add(btnDeselectAll)
        
        btnTags = []
        for tag in initTags():
            btnTags.append(wx.Button(p, -1, tag))
            bsRight.Add(btnTags[-1])
            self.Bind(wx.EVT_BUTTON, self.selectTag(tag), btnTags[-1])
            
        bsRight.Add(wx.StaticLine(p), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)
        bsRight.Add(txtLabel4)
        
        bs.Add(bsLeft, 2, wx.GROW|wx.ALL)
        bs.Add(bsRight)

        p.SetSizer(bs)

        self.Bind(wx.EVT_BUTTON, self.selectAll(True), btnSelectAll)
        self.Bind(wx.EVT_BUTTON, self.selectAll(False), btnDeselectAll)
        self.Bind(wx.EVT_CHECKBOX, grid.resetColor, self.cbxColor)
        self.refreshDisplay()
        
    def refreshDisplay(self):
        self.txtPointAll.SetLabel("%.3f" % (calculate(range(len(grade)))))
        slist = []
        credit = creditS = 0
        for i in xrange(len(grade)):
            credit += int(float(self.grid.GetCellValue(i, 5)))
            if self.grid.GetCellValue(i, self.grid.GetNumberCols() - 1) != '':
                slist.append(int(self.grid.GetCellValue(i,0)) - 1)
                creditS += int(float(self.grid.GetCellValue(i, 5)) + 0.5)
        logging.debug(slist)
        self.txtPointSpec.SetLabel("%.3f" % (calculate(slist)))
        self.txtCredit.SetLabel("%d" % credit)
        self.txtCreditS.SetLabel("%d" % creditS)

    def selectAll(self, selected):
        if selected:
            value = u'是'
        else:
            value = ''
            
        def select(evt):
            col = self.grid.table.GetNumberCols() - 1
            for i in xrange(len(grade)):
                self.grid.SetCellValue(i, col, value)
            
            self.refreshDisplay()

        return select

    def selectTag(self, tag):
        def select(evt):
            col = self.grid.table.GetNumberCols() - 1
            for i, line in enumerate(self.grid.table.data):
                if line[3].upper().startswith(tag):
                    self.grid.SetCellValue(i, col, u'是')
            
            self.refreshDisplay()
            
        return select
            
def initGrade():
    for i, line in enumerate(data[1:]):
        x = float(line[5])
        y = conf.letterToPoint(line[-1])
        grade.append((x, y))

def initTags():
    tags = set()
    for line in data[1:]:
        lesson = line[3].upper()
        lesson = filter(str.isalpha, lesson)
        if len(lesson) > 1:
            tags.add(lesson)
            
    return tags
    
def calculate(list):   # Poor performance, but we don't care
    scores = 0.0
    credits = 0.0
    for i in list:
        if grade[i][1] < 0: continue
        scores += grade[i][0] * grade[i][1]
        credits += grade[i][0]
       
    if abs(credits) < 0.05:
        return 0
    return scores / credits
    
